<?php

namespace fakis\core\base;

use yii\helpers\StringHelper;

/**
 * 枚举
 *
 * ## 定义枚举条目
 * 每个常量作为1个条目
 *
 * ```php
 * final class FruitEnum extends Enum
 * {
 *     public const APPLE = 'apple';
 *     public const BANANA = 'banana';
 *     public const PEAR = 'pear';
 *     public const PINEAPPLE = 'pineapple';
 *     public const STRAWBERRY = 'strawberry';
 * }
 * ```
 *
 * ## 定义枚举用例
 * 每个枚举实例有一个默认用例，命名为`case`，而且我们必须定义默认用例
 *
 * ```php
 * public static function case()
 * {
 *     return [
 *         self::APPLE => '苹果',
 *         self::BANANA => '香蕉',
 *         self::PEAR => '梨子',
 *         self::PINEAPPLE => '凤梨',
 *         self::STRAWBERRY => '草莓',
 *     ];
 * }
 * ```
 *
 * 在业务逻辑中，我们可以调用通用接口
 * ```php
 * FruitEnum::APPLE
 * FruitEnum::map();
 * FruitEnum::value(FruitEnum::APPLE);
 * FruitEnum::indexes();
 * ```
 *
 * ## 定义枚举多用例
 * 我们只需新增多个不同的用例
 * ```php
 * public static function caseYellow()
 * {
 *     return [
 *         self::BANANA => '香蕉',
 *         self::PEAR => '梨子',
 *         self::PINEAPPLE => '凤梨',
 *     ];
 * }
 * ```
 *
 * 同样在业务逻辑中，我们可以调用通用接口
 * ```php
 * FruitEnum::map('yellow');
 * FruitEnum::value(FruitEnum::APPLE, 'yellow');
 * FruitEnum::indexes('yellow');
 * ```
 *
 * @author Fakis <fakis738@qq.com>
 */
abstract class Enum
{
    /**
     * 返回默认用例
     * @return array
     */
    abstract public static function case(): array;

    /**
     * 返回用例指定值
     * @param string $index 索引值
     * @param string $case 指定用例名，没有指定返回默认用例
     * @return mixed|null
     */
    public static function value(string $index, string $case = '')
    {
        return static::map($case)[$index] ?? null;
    }

    /**
     * 返回用例的索引值
     * @param string $case 指定用例名，没有指定返回默认用例
     * @return array
     */
    public static function indexes(string $case = ''): array
    {
        return array_keys(static::map($case));
    }

    /**
     * 返回指定用例
     * @param string $case 指定用例名，没有指定返回默认用例
     * @return array
     */
    public static function map(string $case = '')
    {
        return !empty(static::maps()[$case]) ? forward_static_call(static::maps()[$case]) : [];
    }

    /**
     * 返回所有用例映射
     * @return array
     */
    public static function maps(): array
    {
        $maps = [];

        $ref = new \ReflectionClass(static::class);
        foreach ($ref->getMethods() as $method) {
            if (StringHelper::startsWith($method->name, 'case')) {
                $name = lcfirst(mb_substr($method->name, 4));
                $maps[$name] = [static::class, $method->name];
            }
        }

        return $maps;
    }
}